package com.kehutong.admin.service;

import java.util.*;
import java.util.stream.Collectors;

import org.coraframework.authz.Subject;
import org.coraframework.authz.SubjectUtil;
import org.coraframework.boot.Application;
import org.coraframework.cache.Cache;
import org.coraframework.cache.support.NOCache;
import org.coraframework.config.Config;
import org.coraframework.inject.Inject;
import org.coraframework.inject.Singleton;
import org.coraframework.orm.EntityCache;
import org.coraframework.orm.Pool;
import org.coraframework.orm.exception.ORMException;
import org.coraframework.orm.jdbc.JdbcSession;
import org.coraframework.orm.jdbc.execute.DataFilter;
import org.coraframework.util.Objects;

import com.kehutong.admin.entity.Dict;
import com.kehutong.admin.entity.Menu;
import com.kehutong.admin.entity.Role;
import com.kehutong.admin.util.SysDict;
import com.kehutong.admin.util.TreeEntityUtil;
import com.kehutong.admin.util.service.UserService;
import com.kehutong.common.entity.Root;

@Singleton
public class UserHelper {

    public static final String CACHE_ROLE_LIST = "roleList";
    
    
    public static final String CACHE_MENU_TREE = "menuTree";

    public static final String CACHE_PERMISSIONS = "permissions";
    
    @Inject
    private JdbcSession jdbcSession;
    @Inject
    private UserService userService;
    /**
     * 获取当前登录用户ID
     */
    public String getUserId() {
        return SubjectUtil.getSubject().getUserId();
    }

    /**
     * 获取当前登录用户角色列表
     * @throws Exception 
     */
    public Collection<Role> listRole(Collection<String> roles) throws Exception {
    	Subject subject = SubjectUtil.getSubject();
    	
    	if (Objects.isEmpty(subject.getUserId())) {
            return null;
        }
    	
		Collection<Role> roleList = subject.getAttr(CACHE_ROLE_LIST);
        if (Objects.nonNull(roleList)) {
            return roleList;
        }
        
        if (Root.isRoot(subject.getUserId())) {
   	 		roleList = loadRoles();
        } else {
       	 	roleList = loadRoles(roles);
        }

        subject.setAttr(CACHE_ROLE_LIST, roleList);
        return roleList;
    }


    /** 获取当前登录用户菜单列表
     * @throws Exception */
	public Map<String, Menu> listMenu(Collection<String> roles, boolean showAll) throws Exception {
		Subject subject = SubjectUtil.getSubject();
    	if (Objects.isEmpty(subject.getUserId())) {
            return null;
        }

        Map<String, Menu> menuList;
        if (Root.isRoot(subject.getUserId())) {
            menuList = loadMenus(showAll);
        } else {
        	Collection<Role> roleList = listRole(roles);
            menuList = loadMenus(roleList, showAll);
        }
        
        return menuList;
    }
    
    /** 获取当前登录用户菜单树
     * @throws Exception */
    public Menu menuTree(Collection<String> roles, boolean showAll) throws Exception {
    	Subject subject = SubjectUtil.getSubject();

		Menu root = subject.getAttr(CACHE_MENU_TREE);
		if (Objects.nonNull(root)) {
			return root;
		}

        Map<String, Menu> menuList = listMenu(roles, showAll);
        if (Objects.nonNull(menuList)) {
			root = TreeEntityUtil.organizeAsTree(menuList, true);

			// 获取权限缓存开关状态
			Config config = Application.getConfig("web");
			Boolean enable = config.getBoolean("menu_cache_enable");
			if(enable){
				subject.setAttr(CACHE_MENU_TREE, root);
			}
        }
        
        return root;
    }

	/** 获取当前登录用户一级菜单ID
	 * @throws Exception */
	public Set<String> getTopMenuIds(Collection<String> roles) throws Exception {
		Subject subject = SubjectUtil.getSubject();
		if (Objects.isEmpty(subject.getUserId())) {
			return null;
		}

		Set<String> list = new HashSet<>();
		if (Root.isRoot(subject.getUserId())) {
			Cache<String, Menu> dataCache = checkLoadMenus();
			Set<String> ids = dataCache.values().stream().map(item -> {
				if(Root.isRoot(item.getParent_id())){
					return item.getId();
				}else{
					return null;
				}
			}).collect(Collectors.toSet());
			list.addAll(ids);
		} else {
			Collection<Role> roleList = listRole(roles);
			roleList.forEach(role->{
				role.getMenus().forEach(menuId->{
					Menu menu = Pool.get(Menu.class, menuId);
					if (menu != null && !menu.isDeleted() && Root.isRoot(menu.getParent_id())){
						list.add(menuId);
					}
				});
			});
		}

		return list;
	}

	/** 获取当前登录用户的菜单ID
	 * @throws Exception */
	public Set<String> getMenuIds(Collection<String> roles, boolean showAll) throws Exception {
		Subject subject = SubjectUtil.getSubject();
		if (Objects.isEmpty(subject.getUserId())) {
			return null;
		}

		Set<String> list = new HashSet<>();
		if (Root.isRoot(subject.getUserId())) {
			Cache<String, Menu> dataCache = checkLoadMenus();
			Set<String> ids = dataCache.values().stream().map(Menu::getId).collect(Collectors.toSet());
			list.addAll(ids);
		} else {
			Collection<Role> roleList = listRole(roles);
			roleList.forEach(role->{
				role.getMenus().forEach(menuId->{
					Menu menu = Pool.get(Menu.class, menuId);
					if (menu != null && !menu.isDeleted() && menu.showAll(showAll) ){
						list.add(menuId);
					}
				});
			});
		}

		return list;
	}


    /**
     * 获取用户菜单权限信息
     *
     * @return
     * @throws Exception 
     */
    public Map<String, DataFilter> getPermissions(boolean isAdmin, List<String> roles, boolean forceLoad) throws Exception {
    	Subject subject = SubjectUtil.getSubject();
    	Map<String, DataFilter> permissions = subject.getAttr(CACHE_PERMISSIONS);
        if (permissions == null || forceLoad) {
        	permissions = userService.loadPermission(isAdmin, roles);

			// 获取权限缓存开关状态
			Config config = Application.getConfig("web");
			Boolean enable = config.getBoolean("menu_cache_enable");
			if(enable){
				subject.setAttr(CACHE_PERMISSIONS, permissions);
			}

        }
		permissions.remove("sys:menu:edit");
    	
        return permissions;
    }
    
	/**
     * 根据字典值获取该值的描述
     *
     * @param value 字典值
     * @param type  字典类型
     * @return
	 * @throws Exception 
     */
    public String getDictLabel(Object value, String type) throws Exception {
        return getDictLabel(value, type, "");
    }

    /**
     * 根据字典值获取该值的描述
     *
     * @param value        字典值
     * @param type         字典类型
     * @param defaultValue 默认值
     * @return
     * @throws Exception 
     */
    public String getDictLabel(Object value, String type, String defaultValue) throws Exception {
        if (Objects.nonEmpty(type) && Objects.nonEmpty(String.valueOf(value))) {
            for (SysDict dict : getDictList(type)) {
                if (String.valueOf(value).equals(dict.getValue())) {
                    return dict.getLabel();
                }
            }
        }
        return defaultValue;
    }

    /**
     * 获取该类型对应的字典列表
     *
     * @param type 字典类型
     * @return
     * @throws Exception 
     */
    public List<SysDict> getDictList(String type) throws Exception {
        List<SysDict> dictList = getDictMap().get(type);
        if (dictList == null) {
            dictList = new ArrayList<>();
        }
        return dictList;
    }

    public Map<String, List<SysDict>> getDictMap() throws Exception {
        Map<String, List<SysDict>> dictMap =  new HashMap<>();
        
        List<Dict> dicts = jdbcSession.findArray(Dict.class)
        		.eq("deleted", false)	
        		.exe();
        
        for (Dict dict :dicts ) {
        	List<SysDict> dictList = dictMap.get(dict.getType());
        	if (dictList == null) {
        		dictList = new ArrayList<>();
        		dictMap.put(dict.getType(), dictList);
        	}
        	
        	dictList.add(new SysDict(dict.getLabel(), dict.getValue()));
        }
        
        return dictMap;
    }
    

    // =============================================================================
    // 加载所有的角色
    // =============================================================================
    
    public Collection<Role> loadRoles() {
    	EntityCache<Role> entityCache = jdbcSession.getTemplate(Role.class);
    	//没有缓存直接返回
    	if (entityCache == null) {
    		throw new ORMException("no entity cache found for class:" + Role.class);
    	}
    	
    	Cache<String, Role> dataCache = entityCache.getCache();
    	if (Objects.isNull(dataCache)) {
    		entityCache.setCache(dataCache = new NOCache<>());
    		jdbcSession.findArray(Role.class)
				.eq("deleted", false)
				.exe();
    	}
    	
    	return Collections.unmodifiableCollection(dataCache.values());
    }
    

	public List<Role> loadRoles(Collection<String> roleList) throws Exception {
		if (Objects.isEmpty(roleList)) {
			return Collections.emptyList();
		}
		
		List<Role> roles = new ArrayList<>();
		roleList.forEach(roleId->{
			Role role = Pool.get(Role.class, roleId);
			if (role != null) {
				roles.add(role);
			}
		});
		
		return roles;
	}
	
    // =============================================================================
    // 加载所有的角色
    // =============================================================================
	public Map<String, Menu> loadMenus(boolean showAll) throws Exception {
    	Cache<String, Menu> dataCache = checkLoadMenus();
    	
    	Map<String, Menu> map = new HashMap<>(dataCache.size());
    	dataCache.values().forEach(menu->{
    		Menu mn = Pool.get(Menu.class, menu.getId());
    		map.put(menu.getId(), mn);
    	});
    	
    	return map;
	}
	
	public Map<String, Menu> loadMenus(Collection<Role> roleList,  boolean showAll) throws Exception {
		if (Objects.isEmpty(roleList)) {
			return Collections.emptyMap();
		}
		
		checkLoadMenus();
		
		Map<String, Menu> map = new LinkedHashMap<>(255);
		Set<String> menuIds = new HashSet<>();
		roleList.forEach(role->{
			menuIds.addAll(role.getMenus());
		});

		List<Menu> menus = jdbcSession.findArray(Menu.class)
				.in("id", menuIds)
				.order("sort", "desc")
				.exe();
		menus.forEach(menu -> {
			if (menu != null && !menu.isDeleted() && menu.showAll(showAll)) {
				map.put(menu.getId(), menu);
			}
		});
		return map;
	}
	
	private Cache<String, Menu> checkLoadMenus() {
		EntityCache<Menu> entityCache = jdbcSession.getTemplate(Menu.class);
    	//没有缓存直接返回
    	if (entityCache == null) {
    		throw new ORMException("no entity cache found for class:" + Menu.class);
    	}
    	
    	Cache<String, Menu> dataCache = entityCache.getCache();

    	// 如果緩存开关关闭的也重新获取菜单树
        Config config = Application.getConfig("web");
        Boolean enable = config.getBoolean("menu_cache_enable");

    	if (Objects.isNull(dataCache) || !enable) {
    		entityCache.setCache(dataCache = new NOCache<>());
    		jdbcSession.findArray(Menu.class)
					.order("sort", "desc")
					.eq("deleted", false)
					.exe();
    	}
    	
    	return dataCache;
	}
}
